#include "ListSmile.h"
#include "SmileStruct.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <vector>
#include <algorithm>
#include <functional>

#include <fstream>
#include <iostream>
#include <zlib.h>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTH_LINGO 4
#define LENGHTINLINE 10000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
listSmile::listSmile():filename(NULL), lingoSimilarity(NULL), averageSimDistance(NULL), fileGZ(false) {
  allSmilesMol = new TListE;
}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
listSmile::listSmile(char *filename_tmp): lingoSimilarity(NULL), averageSimDistance(NULL), fileGZ(false) {
  filename = new char[strlen(filename_tmp) + 1];
  strcpy(filename, filename_tmp);
  allSmilesMol = new TListE;
}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
listSmile::~listSmile() {
  if (filename)           delete[] filename;
  if (averageSimDistance) delete[] averageSimDistance;
  if (allSmilesMol)       delete   allSmilesMol;
}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void listSmile::Filename(char *filenameIn) {
  if (filename) delete[] filename;
  filename = new char[strlen(filenameIn) + 1];
  strcpy(filename, filenameIn);
  fileGZ = (strstr(filenameIn, "gz"))?true:false;
}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
char* listSmile::Filename(void) { return filename; }
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void listSmile::FileGZ(bool fileGZIn) {
  fileGZ = fileGZIn;
}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
bool listSmile::FileGZ(void) { return fileGZ; }
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
TListE* listSmile::AllSmiles(void) { return allSmilesMol; }
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::ExtractSmiles(void) {

  if (fileGZ) {
    ExtractGZSmiles();
    return allSmilesMol->Length();
  }

  if (!filename) return 0;

  ifstream  filestr;
  char      lineInput[LENGHTINLINE + 1];

  if (allSmilesMol) delete allSmilesMol;
  allSmilesMol = new TListE;

  filestr.open (filename, ios::in);
  while (!filestr.eof()) {
    filestr.getline(lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      allSmilesMol->Add(tmpSmiles);
    }
  }
  filestr.close();

  return allSmilesMol->Length();

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::ExtractSmiles(char *filenameIn) {

  if (!filenameIn) return 0;
  Filename(filenameIn);
  ExtractSmiles();
  return allSmilesMol->Length();
  
}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::ExtractSmiles(int begin, int amount) {

  if (fileGZ) {
    ExtractGZSmiles(begin, amount);
    return allSmilesMol->Length();
  }

  if (!filename) return 0;

  ifstream filestr;
  char     lineInput[LENGHTINLINE + 1];
  int      numberLine = 0, counter = 0;

  filestr.open(filename, ios::in);
  if (!filestr.is_open()) return 0;

  if (allSmilesMol) delete allSmilesMol;
  allSmilesMol = new TListE;
  
  while (!filestr.eof()) {
      
    filestr.getline(lineInput, LENGHTINLINE);
    
    if ((numberLine++ >= begin) && (counter < amount) && (strlen(lineInput))) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      allSmilesMol->Add(tmpSmiles);
      counter++;
    }
    if (counter >= amount) break;
    
  }
  
  filestr.close();

  return allSmilesMol->Length();

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::ExtractSmiles(char *filenameIn, int begin, int amount) {

  if (!filenameIn) return 0;
  Filename(filenameIn);
  ExtractSmiles(begin, amount);
  return allSmilesMol->Length();

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::ExtractGZSmiles(void) {

  if (!fileGZ) {
    ExtractSmiles();
    return allSmilesMol->Length();
  }

  if (!filename) return 0;

  gzFile filestr = gzopen(filename, "rb9");
  char   lineInput[LENGHTINLINE + 1];

  if (allSmilesMol) delete allSmilesMol;
  allSmilesMol = new TListE;

  while (!gzeof(filestr)) {
    gzgets(filestr, lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      allSmilesMol->Add(tmpSmiles);
    }
  }
  
  gzclose(filestr);

  return allSmilesMol->Length();

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::ExtractGZSmiles(char *filenameIn) {

  if (!filenameIn) return 0;
  Filename(filenameIn);
  ExtractGZSmiles();
  return allSmilesMol->Length();

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::ExtractGZSmiles(int begin, int amount) {

  if (!fileGZ) {
    ExtractSmiles(begin, amount);
    return allSmilesMol->Length();
  }

  if (!filename) return 0;

  gzFile filestr;
  char   lineInput[LENGHTINLINE + 1];
  int    numberLine = 0, counter = 0;

  gzopen(filename, "rb9");

  if (allSmilesMol) delete allSmilesMol;
  allSmilesMol = new TListE;

  while (!gzeof(filestr)) {

    gzgets(filestr, lineInput, LENGHTINLINE);

    if ((numberLine++ >= begin) && (counter < amount) && (strlen(lineInput))) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      allSmilesMol->Add(tmpSmiles);
      counter++;
    }
    if (counter >= amount) break;

  }

  gzclose(filestr);

  return allSmilesMol->Length();

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::ExtractGZSmiles(char *filenameIn, int begin, int amount) {

  if (!filenameIn) return 0;
  Filename(filenameIn);
  ExtractGZSmiles(begin, amount);
  return allSmilesMol->Length();

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::ObtainLingos(void) {

  if (!allSmilesMol) ExtractSmiles();
  for (int i = 0; i < allSmilesMol->Length(); i++)
    ((smileStruct*)allSmilesMol->Get(i))->ObtainLingos();
  return allSmilesMol->Length();

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::ObtainLingos(char *filenameIn) {

  if (!filenameIn) return -1;
  Filename(filenameIn); ExtractSmiles();
  for (int i = 0; i < allSmilesMol->Length(); i++)
    ((smileStruct*)allSmilesMol->Get(i))->ObtainLingos();
  return allSmilesMol->Length();

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::ObtainSimilarityMatrix(void) {

  if (!allSmilesMol->Length()) ObtainLingos();
  if (!allSmilesMol->Length()) return -1;

  lingoSimilarity = new float*[allSmilesMol->Length()];
  for (int i = 0; i < allSmilesMol->Length(); i++) {
    lingoSimilarity[i] = new float;
    for (int j = 0; j < allSmilesMol->Length(); j++) 
      if (i != j) lingoSimilarity[i][j] = lingoSimilarity[j][i] = ObtainLingoSim(((smileStruct*)allSmilesMol->Get(i))->Code(), ((smileStruct*)allSmilesMol->Get(j))->Code());
      else        lingoSimilarity[i][j] = 1;
    
  }

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::ObtainAverageSimDistance(void) {

  ObtainSimilarityMatrix();

  averageSimDistance = new float[allSmilesMol->Length()];
  for (int i = 0; i < allSmilesMol->Length(); i++) {
    averageSimDistance[i] = 0;
    for (int j = 0; j < allSmilesMol->Length(); j++) 
      if (i != j) averageSimDistance[i] += lingoSimilarity[i][j];
    averageSimDistance[i] /= (allSmilesMol->Length() - 1);
  }

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void listSmile::RandomFile(int iteration = 1) {

  if (fileGZ) { RandomGZFile(iteration); return; }
  
  if (!filename) return;

  fstream filestr;
  char    lineInput [LENGHTINLINE + 1];
  
  if (allSmilesMol) delete allSmilesMol;
  allSmilesMol = new TListE;

  //Read the data in input file
  filestr.open(filename, ios::in);

  while (!filestr.eof()) {
    filestr.getline(lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      allSmilesMol->Add(tmpSmiles);
    }
  }
  filestr.close();
  
  //Random process
  filestr.open(filename, ios::out);

  vector <int> randomFlag;
  
  for (int i = 0; i < allSmilesMol->Length(); ++i) randomFlag.push_back(i);
  for (int i = 0; i < iteration; i++)              random_shuffle(randomFlag.begin(), randomFlag.end());
  
  for (int i = 0; i < allSmilesMol->Length(); i++) {
    if (!i) filestr << ((smileStruct*)allSmilesMol->Get(i))->Code() << " " << ((smileStruct*)allSmilesMol->Get(i))->Id();
    else    filestr << endl << ((smileStruct*)allSmilesMol->Get(i))->Code() << " " << ((smileStruct*)allSmilesMol->Get(i))->Id();
  }

  filestr.close();

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void listSmile::RandomGZFile(int iteration = 1) {

  if (!fileGZ) { RandomFile(iteration); return; }

  if (!filename) return;

  gzFile filestr = gzopen(filename, "rb9");;
  char   lineInput[LENGHTINLINE + 1];

  if (allSmilesMol) delete allSmilesMol;
  allSmilesMol = new TListE;

  while (!gzeof(filestr)) {
    gzgets(filestr, lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      allSmilesMol->Add(tmpSmiles);
    }
  }
  gzclose(filestr);

  //Random process
  filestr = gzopen(filename, "wb9");

  vector <int> randomFlag;

  for (int i = 0; i < allSmilesMol->Length(); ++i) randomFlag.push_back(i);
  for (int i = 0; i < iteration; i++)              random_shuffle(randomFlag.begin(), randomFlag.end());

  for (int i = 0; i < allSmilesMol->Length(); i++) {
    if (!i) gzprintf(filestr, "%s %s",   ((smileStruct*)allSmilesMol->Get(i))->Code(), ((smileStruct*)allSmilesMol->Get(i))->Id());
    else    gzprintf(filestr, "\n%s %s", ((smileStruct*)allSmilesMol->Get(i))->Code(), ((smileStruct*)allSmilesMol->Get(i))->Id());
  }

  gzclose(filestr);

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void listSmile::ReadRandomFile(void) {

  if (fileGZ) { ReadRandomGZFile(); return; }

  if (!filename) return;

  fstream filestr;
  char    lineInput[LENGHTINLINE + 1];

  if (allSmilesMol) delete allSmilesMol;
  allSmilesMol = new TListE;

  //Read the data in input file
  filestr.open(filename, ios::in);

  while (!filestr.eof()) {
    filestr.getline(lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      allSmilesMol->Add(tmpSmiles);
    }
  }
  filestr.close();

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void listSmile::ReadRandomGZFile(void) {

  if (!fileGZ) { RandomFile(); return; }

  if (!filename) return;

  gzFile filestr = gzopen(filename, "rb9");;
  char   lineInput[LENGHTINLINE + 1];

  if (allSmilesMol) delete allSmilesMol;
  allSmilesMol = new TListE;

  while (!gzeof(filestr)) {
    gzgets(filestr, lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      allSmilesMol->Add(tmpSmiles);
    }
  }
  gzclose(filestr);

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void listSmile::OrderFile(char *initialFileName, char *nameFileClusterMol, int ancestor) {

  if (fileGZ) { OrderGZFile(initialFileName, nameFileClusterMol, ancestor); return; }
  
  if (!initialFileName && !nameFileClusterMol) return;

  fstream  filestr, clusterStr;
  char     lineInput[LENGHTINLINE + 1];

  if (allSmilesMol) delete allSmilesMol;
  allSmilesMol = new TListE;

  //Read the data in input file
  filestr.open(initialFileName, fstream::in);
  if (!filestr.is_open()) return;
  
  while (!filestr.eof()) {
    filestr.getline(lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      allSmilesMol->Add(tmpSmiles);
    }
  }
  filestr.close();

  int      count = 0;
  
  char ***listMol = new char**[allSmilesMol->Length()];
  for (int i = 0; i < allSmilesMol->Length(); i++) listMol[i] = new char*[ancestor + 1];

  clusterStr.open(nameFileClusterMol, fstream::in);
  if (!clusterStr.is_open()) return;

  while (!clusterStr.eof()) {
    clusterStr.getline(lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      for (int i = 0; i < ancestor + 1; i++) {
        listMol[count][i] = SplitPosLineIdCodeC(lineInput, (i + 1));
        //printf("listMol[%d][%d]=%s\n", count, i, listMol[count][i]);
      }
      count++;
    }
    if (count > allSmilesMol->Length()) break;
  }
  clusterStr.close();

  //Order Process
  filestr.open (filename, fstream::out);

  OrderListAncestor(listMol, allSmilesMol->Length(), ancestor);

  //Order Cluster File
  clusterStr.open(nameFileClusterMol, fstream::out);
  
  for (int i = 0; i < allSmilesMol->Length(); i++) {
    for (int j = 0; j < ancestor + 1; j++) {
      clusterStr << left;
      clusterStr.width(12);
      clusterStr << listMol[i][j] << " ";
    }
    clusterStr << endl;
  }

  clusterStr.close();

  for (int i = 0; i < allSmilesMol->Length(); i++)
    for (int j = 0; j < allSmilesMol->Length(); j++) {
      smileStruct *tmpSmileStruct = (smileStruct*)allSmilesMol->Get(j);
      if (!strcmp(tmpSmileStruct->Id(), listMol[i][0])) {
        char *tmpLineInput = new char[strlen(tmpSmileStruct->Code()) + strlen(tmpSmileStruct->Id()) + 3];
        sprintf(tmpLineInput, "%s %s\0", tmpSmileStruct->Code(), tmpSmileStruct->Id());
        filestr << tmpLineInput << endl;
        break;
      }
    }

  filestr.close();

  for (int i = 0; i < allSmilesMol->Length(); i++) {
    for (int j = 0; j < ancestor + 1; j++) 
      if (listMol[i][j]) delete[] listMol[i][j];
    if (listMol[i]) delete listMol[i];
  }
  if (listMol) delete[] listMol;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void listSmile::OrderGZFile(char *initialFileName, char *nameFileClusterMol, int ancestor) {

  if (!fileGZ) { OrderFile(initialFileName, nameFileClusterMol, ancestor); return; }

  if (!initialFileName && !nameFileClusterMol) return;

  gzFile filestr, clusterStr;
  char   lineInput[LENGHTINLINE + 1];

  if (allSmilesMol) delete allSmilesMol;
  allSmilesMol = new TListE;

  //Read the data in input file
  filestr = gzopen(initialFileName, "rb9");

  while (!gzeof(filestr)) {
    gzgets(filestr, lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      allSmilesMol->Add(tmpSmiles);
    }
  }
  gzclose(filestr);

  int      count = 0;

  char ***listMol = new char**[allSmilesMol->Length()];
  for (int i = 0; i < allSmilesMol->Length(); i++) listMol[i] = new char*[ancestor + 1];

  clusterStr = gzopen(nameFileClusterMol, "rb9");

  while (!gzeof(clusterStr)) {
    gzgets(clusterStr, lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      for (int i = 0; i < ancestor + 1; i++)
        listMol[count][i] = SplitPosLineIdCodeC(lineInput, (i + 1));
      count++;
    }
    if (count > allSmilesMol->Length()) break;
  }
  gzclose(clusterStr);

  //Order Process
  OrderListAncestor(listMol, allSmilesMol->Length(), ancestor);

  //Order Cluster File
  clusterStr = gzopen(nameFileClusterMol, "wb9");

  for (int i = 0; i < allSmilesMol->Length(); i++) {
    for (int j = 0; j < ancestor + 1; j++) 
      gzprintf(clusterStr, "%12s ", listMol[i][j]);
    gzprintf(clusterStr, "\n");
  }

  gzclose(clusterStr);
  filestr = gzopen (filename, "wb9");

  for (int i = 0; i < allSmilesMol->Length(); i++)
    for (int j = 0; j < allSmilesMol->Length(); j++) {
      smileStruct *tmpSmileStruct = (smileStruct*)allSmilesMol->Get(j);
      if (!strcmp(tmpSmileStruct->Id(), listMol[i][0])) {
        gzprintf(filestr, "%s %s\n", tmpSmileStruct->Code(), tmpSmileStruct->Id());
        break;
      }
    }

  gzclose(filestr);

  for (int i = 0; i < allSmilesMol->Length(); i++) {
    for (int j = 0; j < ancestor + 1; j++)
      if (listMol[i][j]) delete[] listMol[i][j];
    if (listMol[i]) delete listMol[i];
  }
  if (listMol) delete[] listMol;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void listSmile::ReduceFile(char *initialFileName, char *nameFileClusterMol) {
    
  if (fileGZ) { ReduceGZFile(initialFileName, nameFileClusterMol); return; }
  
  if (!initialFileName && !nameFileClusterMol) return;

  fstream  filestr, clusterStr;
  char     lineInput[LENGHTINLINE + 1];

  //Read the data in input file
  filestr.open(initialFileName, fstream::in);
  if (!filestr.is_open()) return;

  if (allSmilesMol) delete allSmilesMol;
  allSmilesMol = new TListE;
  
  while (!filestr.eof()) {
    filestr.getline(lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      allSmilesMol->Add(tmpSmiles);
    }
  }
  filestr.close();

  TListE  *ClusterMol = new TListE;

  clusterStr.open(nameFileClusterMol, fstream::in);
  if (!clusterStr.is_open()) return;

  while (!clusterStr.eof()) {
    clusterStr.getline(lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      ClusterMol->Add(tmpSmiles);
    }
  }
  clusterStr.close();

  filestr.open(initialFileName, fstream::out);

  for (int i = 0; i < allSmilesMol->Length(); i++)
    for (int j = 0; j < ClusterMol->Length(); j++) {
        
      smileStruct *tmpAllSmiles  = ((smileStruct*)allSmilesMol->Get(i));
      smileStruct *tmpClusterMol = ((smileStruct*)ClusterMol->Get(j));
      
      if (!strcmp(tmpAllSmiles->Id(), tmpClusterMol->Id())) {
        filestr << tmpAllSmiles->Code() << " " << tmpAllSmiles->Id() << endl;
        break;
      }
      
    }

  filestr.close();

  if (ClusterMol) delete ClusterMol;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void listSmile::ReduceGZFile(char *initialFileName, char *nameFileClusterMol) {

  if (!fileGZ) { ReduceFile(initialFileName, nameFileClusterMol); return; }

  if (!initialFileName && !nameFileClusterMol) return;

  gzFile filestr, clusterStr;
  char   lineInput[LENGHTINLINE + 1];

  //Read the data in input file
  filestr = gzopen(initialFileName, "rb9");

  if (allSmilesMol) delete allSmilesMol;
  allSmilesMol = new TListE;

  while (!gzeof(filestr)) {
    gzgets(filestr, lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      allSmilesMol->Add(tmpSmiles);
    }
  }
  gzclose(filestr);

  TListE  *ClusterMol = new TListE;

  clusterStr = gzopen(nameFileClusterMol, "rb9");

  while (!gzeof(clusterStr)) {
    gzgets(clusterStr, lineInput, LENGHTINLINE);
    if (strlen(lineInput)) {
      smileStruct  *tmpSmiles = new smileStruct;
      tmpSmiles->InputLine(lineInput);
      ClusterMol->Add(tmpSmiles);
    }
  }
  gzclose(clusterStr);

  filestr = gzopen(initialFileName, "wb9");

  for (int i = 0; i < allSmilesMol->Length(); i++)
    for (int j = 0; j < ClusterMol->Length(); j++) {

      smileStruct *tmpAllSmiles  = ((smileStruct*)allSmilesMol->Get(i));
      smileStruct *tmpClusterMol = ((smileStruct*)ClusterMol->Get(j));

      if (!strcmp(tmpAllSmiles->Id(), tmpClusterMol->Id())) {
        gzprintf(filestr, "%s %s\n", tmpAllSmiles->Code(), tmpAllSmiles->Id());
        break;
      }

    }

  gzclose(filestr);

  if (ClusterMol) delete ClusterMol;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::SplitPosLineIdCode(char *line, int pos) {

  char *pch, *lineTmp = new char[strlen(line) + 1];
  int   count = 1;

  strcpy(lineTmp, line);

  pch = strtok(lineTmp, " ");
  if (pos == 1) return atoi(pch);

  while (pch != NULL) {
    pch = strtok(NULL, " ");
    if (++count == pos) { delete[] lineTmp; return atoi(pch); }
  }

  delete[] lineTmp;

  return -1;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
char* listSmile::SplitPosLineIdCodeC(char *line, int pos) {

  char *pch, *lineTmp = new char[strlen(line) + 1];
  int   count = 1;

  strcpy(lineTmp, line);

  pch = strtok(lineTmp, " ");
  if (pos == 1) {
    char *result = new char[strlen(pch) + 1];
    strcpy(result, pch);
    delete[] lineTmp;
    return result;
  }

  while (pch != NULL) {
    pch = strtok(NULL, " ");
    if (++count == pos) {
      char *result = new char[strlen(pch) + 1];
      strcpy(result, pch);
      delete[] lineTmp;
      return result;
    }
  }

  delete[] lineTmp;

  return NULL;

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::OrderListAncestor(int **&listMol, int numberSmiles, int ancestor) {

  for (int i = 0; i < ancestor + 1; i++) {

    int *initial = NULL, *final = NULL, numberCicles = 0;

    if (!i) {

      numberCicles  = 1;
      initial       = new int;
      *initial      = 0;
      final         = new int;
      *final        = numberSmiles;
      
    }
    else {
        
      int tmpValue = -1, *listMol_tmp = new int[numberSmiles];
      
      for (int j = 0; j < numberSmiles; j++) 
        listMol_tmp[j] = listMol[j][ancestor - i + 1];

      for (int j = 0; j < numberSmiles; j++) 
        if (listMol_tmp[j] != tmpValue) {
          tmpValue = listMol_tmp[j];
          numberCicles++;
        }

      initial = new int[numberCicles];
      final   = new int[numberCicles];

      numberCicles =  0;
      tmpValue  = -1;
      
      for (int j = 0; j < numberSmiles; j++) {
        if (listMol_tmp[j] != tmpValue) {
          if (numberCicles)
            final[numberCicles - 1] = j;
          initial[numberCicles++] = j;
          tmpValue = listMol_tmp[j];
        }
      }
      
      final[numberCicles - 1] = numberSmiles;
      
      delete[] listMol_tmp;

    }

    if (!initial || !final) continue;
    
    for (int z = 0; z < numberCicles; z++) {
        
      for (int j = initial[z]; j < final[z] - 1; j++)
        for (int k = j; k < final[z]; k++) {
          if (listMol[k][ancestor - i] < listMol[j][ancestor - i]) {
            int  *LisMol_tmp = listMol[k];
            listMol[k]       = listMol[j];
            listMol[j]       = LisMol_tmp;
          }
        }
    }

    delete[] initial;
    delete[] final;

  }
    
}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
int listSmile::OrderListAncestor(char ***&listMol, int numberSmiles, int ancestor) {

  for (int i = 0; i < ancestor + 1; i++) {

    int *initial = NULL, *final = NULL, numberCicles = 0;

    if (!i) {
      numberCicles  = 1;
      initial       = new int;
      *initial      = 0;
      final         = new int;
      *final        = numberSmiles;
    }
    else {

      char *tmpValue = NULL, **listMol_tmp = new char*[numberSmiles];

      for (int j = 0; j < numberSmiles; j++) {
        listMol_tmp[j] = new char[strlen(listMol[j][ancestor - i + 1]) + 1];
        strcpy(listMol_tmp[j], listMol[j][ancestor - i + 1]);
      }

      for (int j = 0; j < numberSmiles; j++)
        if (!tmpValue) {
          tmpValue = new char[strlen(listMol_tmp[j]) + 1];
          strcpy(tmpValue, listMol_tmp[j]);
          numberCicles++;
        }
        else if (strcmp(listMol_tmp[j], tmpValue) != 0) {
          if (tmpValue) delete[] tmpValue;
          tmpValue = new char[strlen(listMol_tmp[j]) + 1];
          strcpy(tmpValue, listMol_tmp[j]);
          numberCicles++;
        }

      initial = new int[numberCicles];
      final   = new int[numberCicles];

      numberCicles =  0;
      if (tmpValue) delete[] tmpValue;
      tmpValue     = NULL;

      for (int j = 0; j < numberSmiles; j++) {
        if (!tmpValue) {
          if (numberCicles)
            final[numberCicles - 1] = j;
          initial[numberCicles++] = j;
          tmpValue = new char[strlen(listMol_tmp[j]) + 1];
          strcpy(tmpValue, listMol_tmp[j]);
        }
        if (strcmp(listMol_tmp[j], tmpValue) != 0) {
          if (numberCicles)
            final[numberCicles - 1] = j;
          initial[numberCicles++] = j;
          if (tmpValue) delete[] tmpValue;
          tmpValue = new char[strlen(listMol_tmp[j]) + 1];
          strcpy(tmpValue, listMol_tmp[j]);
        }
      }

      final[numberCicles - 1] = numberSmiles;

      for (int i = 0; i < numberSmiles; i++)
        if (listMol_tmp[i]) delete[] listMol_tmp[i];
      if (listMol_tmp) delete[] listMol_tmp;

      if (tmpValue) delete[] tmpValue;

    }

    if (!initial || !final) continue;

    for (int z = 0; z < numberCicles; z++) {

      for (int j = initial[z]; j < final[z] - 1; j++)
        for (int k = j; k < final[z]; k++) {
          if (strcmp(listMol[k][ancestor - i], listMol[j][ancestor - i]) < 0) {
            char **LisMol_tmp = listMol[k];
            listMol[k]        = listMol[j];
            listMol[j]        = LisMol_tmp;
          }
        }
    }

    delete[] initial;
    delete[] final;

  }

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void listSmile::MixArray(long *array, int n) {

  long *array1, *array2;
  int n1, n2, i, j;

  if (n > 1) {
    if (n%2 == 0)   n1 = n2 = (int)n/2;
    else         {  n1 = (int)n/2; n2 = n1 + 1; }

    array1  = new long[n1];
    array2  = new long[n2];

    for (i = 0; i < n1; i++)      array1[i] = array[i];
    for (j = 0; j < n2; i++, j++) array2[j] = array[i];

    MixArray(array1, n1);
    MixArray(array2, n2);
    MixArrayX(array1, n1, array2, n2, array);

    delete[] array1;
    delete[] array2;

  }

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void listSmile::MixArrayX(long *array1, int n1, long *array2, int n2, long *array3) {

  int x1 = 0, x2 = 0, x3 = 0;

  while (x1 < n1 && x2 < n2)
    if (array1[x1] < array2[x2])  array3[x3++] = array1[x1++];
    else                          array3[x3++] = array2[x2++];
  while (x1 < n1) array3[x3++] = array1[x1++];
  while (x2 < n2) array3[x3++] = array2[x2++];

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
void listSmile::MergeSort(long *array, int n) {

  MixArray(array, n);

}
//---------------------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------------------
float listSmile::ObtainLingoSim(char *lingoString1, char *lingoString2) {

  int length1 = strlen(lingoString1), length2 = strlen(lingoString2);
  char tmpLingo1[length1], tmpLingo2[length2];
  int  i, j, k, z;

  //Change the Cl bu L and Br by R in the two lingo sequences
  for (i = 0, j = 0, k = 0; i < ((length1 > length2)?length1:length2); i++) {
    if (i < length1) {
      if      ((lingoString1[i] == 'l') || (lingoString1[i] == 'r')) tmpLingo1[j - 1] = (lingoString1[i] == 'l')?'L':'R';
      else if (isdigit(lingoString1[i])) tmpLingo1[j++] = '?';
      else tmpLingo1[j++] = lingoString1[i];
    }
    if (i < length2) {
      if ((lingoString2[i] == 'l') || (lingoString2[i] == 'r')) tmpLingo2[k - 1] = (lingoString2[i] == 'l')?'L':'R';
      else if (isdigit(lingoString2[i])) tmpLingo2[k++] = '?';
      else tmpLingo2[k++] = lingoString2[i];
    }
  }
  tmpLingo1[j] = '\0'; tmpLingo2[k] = '\0';

  j -= (LENGTH_LINGO - 1); k -= (LENGTH_LINGO - 1);
  int    L = j + k; //Total Lingos in two sequences
  float similarityValue = 0, Nai, Nbi;

  for (i = 0; i < j + k; i++) {

    char   compare[5];
    bool   analize = true;

    Nai = 0, Nbi = 0;

    if (i < j) {

      strncpy(compare, tmpLingo1 + i, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          if (z < i) {
            analize = false;
            break;
          }
          Nai++;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          Nbi++;
        }
      }
    }
    else {

      strncpy(compare, tmpLingo2 + i - j, 4);
      compare[4] = '\0';

      for (z = 0; z < ((j > k)?j:k); z++) {
        if (z < j && !strncmp(compare, tmpLingo1 + z, 4)) {
          analize = false;
          break;
        }
        if (z < k && !strncmp(compare, tmpLingo2 + z, 4)) {
          if (z < i - j) {
            analize = false;
            break;
          }
          Nbi++;
        }
      }
    }

    if (analize == true) similarityValue += (((Nai - Nbi) >= 0)?(1 - ((Nai - Nbi)/(Nai + Nbi))):(1 - ((Nbi - Nai)/(Nai + Nbi))));
    else                 L--;
  }

  return similarityValue/L;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
